package cn.felord.spring.security.config;

import cn.felord.spring.security.entity.RestBody;
import cn.felord.spring.security.filter.PreLoginFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Slf4j
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class CustomWebSecurityConfig {

    /**
     * The type Default configurer adapter.
     */
    @Configuration
    @Order(SecurityProperties.BASIC_AUTH_ORDER)
    static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Resource
        private PreLoginFilter preLoginFilter;

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            //基于内存，也可以在配置文件进行配置，生产环境一般是基于数据库
            auth.inMemoryAuthentication()
                    .withUser("root").password("{noop}12345").roles("ADMIN", "DBA")
                    .and()
                    .withUser("admin").password("{noop}12345").roles("ADMIN", "USER")
                    .and()
                    .withUser("yangjun").password("{noop}12345").roles("USER");
        }

        @Override
        public void configure(WebSecurity web) {
            super.configure(web);
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .cors()
                    .and()
                    .authorizeRequests().anyRequest().authenticated()
                    .and()
                    .addFilterBefore(preLoginFilter, UsernamePasswordAuthenticationFilter.class)
                    .formLogin().loginProcessingUrl("/process")
                    .successForwardUrl("/login/success")
                    .failureForwardUrl("/login/failure")
                    .and()
                    .exceptionHandling()
                    .accessDeniedHandler((request, response, accessDeniedException) -> {
                        write(response, "accessDeniedHandler");
                    })
                    .authenticationEntryPoint((request, response, authException) -> {
                        write(response, "authenticationEntryPoint");
                    })
                    .and()
                    .logout()
                    .addLogoutHandler((request, response, authentication) -> {
                        User user = (User) authentication.getPrincipal();
                        String username = user.getUsername();
                        log.info("username: {}  is offline now", username);
                    })
                    .logoutSuccessHandler((request, response, authentication) -> {
                        User user = (User) authentication.getPrincipal();
                        String username = user.getUsername();
                        log.info("username: {}  is offline now", username);
                        responseJsonWriter(response, RestBody.ok("退出成功"));
                    });
        }
    }

    private static void write(HttpServletResponse response, String st) {
        try {
            if (response.isCommitted()) {
                log.debug("Response has already been committed");
                return;
            }
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write(st);
            out.flush();
            out.close();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private static void responseJsonWriter(HttpServletResponse response, Object value) throws IOException {
        if (response.isCommitted()) {
            log.debug("Response has already been committed");
            return;
        }
        response.setStatus(HttpServletResponse.SC_OK);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        ObjectMapper objectMapper = new ObjectMapper();
        String resBody = objectMapper.writeValueAsString(value);
        PrintWriter printWriter = response.getWriter();
        printWriter.print(resBody);
        printWriter.flush();
        printWriter.close();
    }
}
